import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types';
import { parseHeaders } from '../helpers/header';
import { transformResponse } from '../helpers/data';
import { createAxiosError } from '../helpers/error';
import { isURLSameOrigin } from '../helpers/url';
import { isFormData } from '../helpers/utils';
import cookie from '../helpers/cookie';
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise((resolve, reject) => {
    const {
      data = null,
      url,
      method = 'get',
      headers = {},
      responseType,
      timeout,
      cancelToken,
      withCredentials,
      xsrfHeaderName,
      xsrfCookieName,
      onDownloadProgress,
      onUploadProgress,
      auth,
      validateStatus,
    } = config;

    const request = new XMLHttpRequest();

    request.open(method.toUpperCase(), url!, true);
    configureRequest();
    addEvents();
    processHeaders();
    processCancel();
    request.send(data);

    function configureRequest(): void {
      if (responseType) {
        request.responseType = responseType;
      }

      if (timeout !== undefined) {
        request.timeout = timeout;
      }
      if (withCredentials) {
        request.withCredentials = withCredentials;
      }
    }
    function addEvents(): void {
      request.onreadystatechange = function() {
        if (request.status === 0) {
          return;
        }

        if (request.readyState === 4) {
          const responseHeaders = request.getAllResponseHeaders();
          const responseData = responseType !== 'text' ? request.response : request.responseText;
          const response: AxiosResponse = {
            data: transformResponse(responseData),
            status: request.status,
            statusText: request.statusText,
            headers: parseHeaders(responseHeaders),
            config,
            request,
          };
          handleResponse(response);
        }
      };
      request.onerror = function handleError() {
        reject(createAxiosError('Network error', config, request, null));
      };
      request.ontimeout = function handleTimeout() {
        reject(
          createAxiosError(`Timeout of ${timeout} ms exceeded`, config, request, 'ECONNABORTED')
        );
      };
      if (onUploadProgress) {
        request.upload.onprogress = onUploadProgress;
      }
      if (onDownloadProgress) {
        request.onprogress = onDownloadProgress;
      }
    }
    function processHeaders(): void {
      if (isFormData(data)) {
        delete headers['Content-Type'];
      }
      if (auth) {
        headers['Authorization'] = 'Basic ' + btoa(auth.username + ':' + auth.password);
      }
      if ((withCredentials || isURLSameOrigin(url!)) && xsrfCookieName && xsrfHeaderName) {
        const xsrfValue = cookie.read(xsrfCookieName);
        if (xsrfValue) {
          headers[xsrfHeaderName] = xsrfValue;
        }
      }
      Object.keys(headers).forEach(key => {
        request.setRequestHeader(key, headers[key]);
      });
    }
    function processCancel(): void {
      if (cancelToken) {
        cancelToken.promise.then(reason => {
          request.abort();
          reject(reason);
        });
      }
    }
    function handleResponse(response: AxiosResponse) {
      if (!validateStatus || validateStatus(response.status)) {
        resolve(response);
      } else {
        reject(
          createAxiosError(
            `Request failed with status code ${request.status}`,
            config,
            request,
            null,
            response
          )
        );
      }
    }
  });
}
